﻿#include "HttpUtils.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QSettings>
#include <QDebug>
#include <QCoreApplication>
#include <memory>
#include "restClient/restclient-cpp/connection.h"
#include <QNetworkAccessManager>
#include "HwUtils.h"

using namespace aoi;

HttpUtils httpUtils;




HttpStatusInfo::HttpStatusInfo(int httpCode, QString reason)
{
	this->httpCode = httpCode;
	this->reason = reason;
}

HttpStatusInfo HttpStatusInfo::getByCode(int httpCode)
{
	QMap<int, HttpStatusInfo>  httpCodeMap;
	httpCodeMap.insert(0, HttpStatusInfo(0, QStringLiteral("正常响应")));
	httpCodeMap.insert(1, HttpStatusInfo(1, QStringLiteral("错误的协议")));
	httpCodeMap.insert(2, HttpStatusInfo(2, QStringLiteral("初始化代码失败")));
	httpCodeMap.insert(3, HttpStatusInfo(3, QStringLiteral("URL格式不正确")));
	httpCodeMap.insert(5, HttpStatusInfo(5, QStringLiteral("请求协议错误")));
	httpCodeMap.insert(6, HttpStatusInfo(6, QStringLiteral("无法解析主机地址")));
	httpCodeMap.insert(7, HttpStatusInfo(7, QStringLiteral("无法连接到主机")));
	httpCodeMap.insert(8, HttpStatusInfo(8, QStringLiteral("远程服务器不可用")));
	httpCodeMap.insert(9, HttpStatusInfo(9, QStringLiteral("访问资源错误")));
	httpCodeMap.insert(10, HttpStatusInfo(10, QStringLiteral("FTP会话接收错误")));
	httpCodeMap.insert(11, HttpStatusInfo(11, QStringLiteral("FTP密码错误")));
	httpCodeMap.insert(12, HttpStatusInfo(12, QStringLiteral("FTP会话连接超时")));
	httpCodeMap.insert(13, HttpStatusInfo(13, QStringLiteral("FTP结果错误")));
	httpCodeMap.insert(14, HttpStatusInfo(14, QStringLiteral("FTP回应PASV命令")));
	httpCodeMap.insert(15, HttpStatusInfo(15, QStringLiteral("FTP服务内部故障")));
	httpCodeMap.insert(17, HttpStatusInfo(17, QStringLiteral("FTP无法设定为二进制传输")));
	httpCodeMap.insert(18, HttpStatusInfo(18, QStringLiteral("FTP文件传输短或大于预期")));
	httpCodeMap.insert(19, HttpStatusInfo(19, QStringLiteral("FTP不能下载/访问给定的文件")));
	httpCodeMap.insert(21, HttpStatusInfo(21, QStringLiteral("FTP quote 错误")));
	httpCodeMap.insert(22, HttpStatusInfo(22, QStringLiteral("HTTP找不到网页")));
	httpCodeMap.insert(23, HttpStatusInfo(23, QStringLiteral("本地文件写入失败")));
	httpCodeMap.insert(25, HttpStatusInfo(25, QStringLiteral("FTP服务无法STOR文件")));
	httpCodeMap.insert(26, HttpStatusInfo(26, QStringLiteral("读取本地文件错误")));
	httpCodeMap.insert(27, HttpStatusInfo(27, QStringLiteral("内存分配请求失败")));
	httpCodeMap.insert(28, HttpStatusInfo(28, QStringLiteral("访问超时")));
	httpCodeMap.insert(30, HttpStatusInfo(30, QStringLiteral("FTP端口错误")));
	httpCodeMap.insert(31, HttpStatusInfo(31, QStringLiteral("FTP无法使用REST命令")));
	httpCodeMap.insert(33, HttpStatusInfo(33, QStringLiteral("不支持此类HTTP请求")));
	httpCodeMap.insert(34, HttpStatusInfo(34, QStringLiteral("内部POST请求错误")));
	httpCodeMap.insert(35, HttpStatusInfo(35, QStringLiteral("SSL/TLS握手失败")));
	httpCodeMap.insert(36, HttpStatusInfo(36, QStringLiteral("FTP续传损坏")));
	httpCodeMap.insert(37, HttpStatusInfo(37, QStringLiteral("文件无权限读取")));
	httpCodeMap.insert(38, HttpStatusInfo(38, QStringLiteral("LDAP无法绑定")));
	httpCodeMap.insert(39, HttpStatusInfo(39, QStringLiteral("LDAP搜索失败")));
	httpCodeMap.insert(41, HttpStatusInfo(41, QStringLiteral("无法找到必要的LDAP 功能")));
	httpCodeMap.insert(42, HttpStatusInfo(42, QStringLiteral("回调终止")));
	httpCodeMap.insert(43, HttpStatusInfo(43, QStringLiteral("不正确参数调用引发了内部错误")));
	httpCodeMap.insert(45, HttpStatusInfo(45, QStringLiteral("指定的外发接口无法使用")));
	httpCodeMap.insert(47, HttpStatusInfo(47, QStringLiteral("过多的重定向")));
	httpCodeMap.insert(51, HttpStatusInfo(51, QStringLiteral("远程服务器SSL证书错误")));
	httpCodeMap.insert(52, HttpStatusInfo(52, QStringLiteral("服务器无返回内容")));
	httpCodeMap.insert(53, HttpStatusInfo(53, QStringLiteral("加密引擎未找到")));
	httpCodeMap.insert(54, HttpStatusInfo(54, QStringLiteral("设定默认SSL加密失败")));
	httpCodeMap.insert(55, HttpStatusInfo(55, QStringLiteral("无法发送网络数据")));
	httpCodeMap.insert(56, HttpStatusInfo(56, QStringLiteral("在接收网络数据时失败")));
	httpCodeMap.insert(58, HttpStatusInfo(58, QStringLiteral("本地客户端证书错误")));
	httpCodeMap.insert(59, HttpStatusInfo(59, QStringLiteral("SSL证书密码错误")));
	httpCodeMap.insert(60, HttpStatusInfo(60, QStringLiteral("CA证书验证失败")));
	httpCodeMap.insert(61, HttpStatusInfo(61, QStringLiteral("无效的LDAP URL")));
	httpCodeMap.insert(63, HttpStatusInfo(63, QStringLiteral("超过最大文件尺寸")));
	httpCodeMap.insert(64, HttpStatusInfo(64, QStringLiteral("要求的FTP的SSL失败")));
	httpCodeMap.insert(66, HttpStatusInfo(66, QStringLiteral("初始化SSL 引擎失败")));
	httpCodeMap.insert(67, HttpStatusInfo(67, QStringLiteral("服务器拒绝登录")));
	httpCodeMap.insert(77, HttpStatusInfo(77, QStringLiteral("CA证书权限读取不足")));
	httpCodeMap.insert(78, HttpStatusInfo(78, QStringLiteral("URL中引用资源不存在")));
	httpCodeMap.insert(79, HttpStatusInfo(79, QStringLiteral("SSH会话错误")));
	httpCodeMap.insert(80, HttpStatusInfo(80, QStringLiteral("未能关闭SSL连接")));
	httpCodeMap.insert(200, HttpStatusInfo(200, QStringLiteral("正常响应")));
	httpCodeMap.insert(-1, HttpStatusInfo(-1, QStringLiteral("未知原因")));
	if (httpCodeMap.contains(httpCode))
	{
		return httpCodeMap.find(httpCode).value();
	}
	return httpCodeMap.find(-1).value();

}


HttpUtils::HttpUtils()
{	
	initParams();
}

HttpUtils::~HttpUtils()
{
}

void HttpUtils::initParams(){
	readSSLParams(m_SslParam);
	setHostUrl(readGlobalIniConfig(GLOBAL_HOST_URL));
}

void HttpUtils::setToken(QString token)
{
	m_Token = token;
}

QString HttpUtils::getTokenOrDefault(QString currentToken)
{
	if (currentToken == nullptr || currentToken == "")
	{
		return readGlobalIniConfig(GLOBAL_YT_LOGIN_TOKEN);
	}
	return currentToken;
}

QString HttpUtils::getToken()
{
	return getTokenOrDefault(m_Token);
}

void HttpUtils::setHostUrl(QString hostUrl)
{
	m_HostUrl = hostUrl;
}

QString HttpUtils::getHostUrl()
{
	return m_HostUrl;
}

void HttpUtils::setSerialNumber(QString serialNumber)
{
	m_SerialNumber = serialNumber;
}

/// <summary>
/// get table SerialNumber
/// </summary>
/// <returns></returns>
QString HttpUtils::getSerialNumber()
{
	return m_SerialNumber;
}

void HttpUtils::setScene(QString sceneName)
{
	m_Scene = sceneName;
}

QString HttpUtils::getScene()
{
	return m_Scene;
}

//校验请求基本参数
bool HttpUtils::checkReqParams()
{
	return util::HwUtils::checkSomeParam(QStringLiteral("服务主机URL"), getHostUrl(), QStringLiteral("服务主机URL,请先设置主机URL"))
		&& util::HwUtils::checkSomeParam(QStringLiteral("Token"), getToken(), QStringLiteral("登录信息不能为空，请先登录"));
}


void HttpUtils::sendRequest(QString uri, HttpRequestType reqType, QString& responseBody, int& resCode, QByteArray requestByte /*= ""*/)
{
	resCode = -1;
	if (!checkReqParams())
		return;
	std::unique_ptr<RestClient::Connection> conn = std::make_unique<RestClient::Connection>(m_HostUrl.toLocal8Bit().constData());

	conn->AppendHeader("Content-Type", "application/json");
	conn->AppendHeader("charset", "UTF-8");
	std::string authValue = "Bearer ";
	authValue.append(getToken().toLocal8Bit().constData());
	conn->AppendHeader("Authorization", authValue);

	std::string uuid = util::HwUtils::getBiosId().toLocal8Bit().constData();
	conn->AppendHeader(REQ_HEADER_KEY_UUID, uuid);
	qInfo() << "header_uuid:" << uuid.c_str();
	std::string reqIp = util::HwUtils::read_ip_address().toLocal8Bit().constData();
	conn->AppendHeader(REQ_HEADER_KEY_IP, reqIp);
	qInfo() << "header_ip:" << reqIp.c_str();
	conn->AppendHeader(REQ_HEADER_KEY_VERSION_SOFTWARE, REQ_HEADER_VALUE_VERSION_SOFTWARE);
	qInfo() << "header_vision-software:" << REQ_HEADER_VALUE_VERSION_SOFTWARE;
	conn->AppendHeader(REQ_HEADER_KEY_SCENE_NAME, getScene().toLocal8Bit().constData());
	qInfo() << "header_scene-name:" << getScene().toLocal8Bit().constData();

	conn->SetTimeout(HttpUtils::getConnectionTimeOut());

	conn->SetCAInfoFilePath(m_SslParam.CAFilePath.toLocal8Bit().constData());
	RestClient::Response response;
	qInfo() << "host:" << getHostUrl();
	qInfo() << "uri:" << uri;
	if (util::HwUtils::logDebugRequired())
	{
		qDebug() << "Req:" << requestByte.constData();
	}
	switch (reqType)
	{
	case HttpRequestType::POST:
		qInfo() << "ready to POST req";
		response = conn->post(uri.toLocal8Bit().constData(), requestByte.constData());
		break;
	case HttpRequestType::GET:
		qInfo() << "ready to GET req";
		response = conn->get(uri.toLocal8Bit().constData());
		break;
	case HttpRequestType::DEL:
		response = conn->del(uri.toLocal8Bit().constData());
		break;
	case HttpRequestType::PUT:
		response = conn->post(uri.toLocal8Bit().constData(), requestByte.constData());
	default:
		qCritical() << "can not find HttpRequestType!"<<endl;
		break;
	}
	responseBody = QString::fromLocal8Bit(response.body.c_str(), response.body.size());
	if (util::HwUtils::logDebugRequired())
	{
		qDebug() << "Rsp: " << responseBody;
	}
	resCode = response.code;
}


int HttpUtils::queryProductsBySerialNumber(QueryProductListResult& queryResult)
{
	int resCode;
	QString resbody;
	QString uri = QStringLiteral("/iqi/api/v1/product-manage/product/list-by-station?SerialNumber=%1").arg(getSerialNumber());

	sendRequest(uri, HttpRequestType::GET, resbody, resCode);

	QJsonDocument responseJson = QJsonDocument::fromJson(resbody.toLocal8Bit().constData());
	QJsonObject bodyObj = responseJson.object();
	queryResult.errorCode = bodyObj.value("code").toInt();
	queryResult.descMsg = bodyObj.value("msg").toString();
	QJsonObject dataObject = bodyObj.value("data").toObject();
	queryResult.productNum = dataObject.value("total_num").toString();
	queryResult.ProductList.clear();
	QJsonArray productArray = dataObject.value("products").toArray();
	for each (auto& product in productArray)
	{
		queryResult.ProductList.append(product.toString());
	}
	return resCode;
}

int HttpUtils::queryAiUrl(QueryAiUrlReq& queryAiUrlReq, QueryAiUrlRes& queryRes)
{
	int resCode;
	QString resbody;
	qInfo() << "[GET]SerialNumber=" << getSerialNumber() << ",ProductName=" << queryAiUrlReq.ProductName << ",SceneName=" << queryAiUrlReq.SceneName;
	QString uri = QStringLiteral("/iqi/ProjectService/V2/project?SerialNumber=").append(getSerialNumber())
		.append("&ProductName=").append(queryAiUrlReq.ProductName)
		.append("&SceneName=").append(queryAiUrlReq.SceneName);

	httpUtils.setScene(queryAiUrlReq.SceneName);
	sendRequest(uri, HttpRequestType::GET, resbody, resCode);

	QJsonDocument responseJson = QJsonDocument::fromJson(resbody.toLocal8Bit().constData());
	QJsonObject bodyObj = responseJson.object();
	queryRes.errorCode = bodyObj.value("code").toInt();
	queryRes.descMsg = bodyObj.value("msg").toString();
	QJsonObject dataObject = bodyObj.value("data").toObject();
	queryRes.ProjectName = dataObject.value("ProjectName").toString();
	queryRes.TaskName = dataObject.value("TaskName").toString();
	queryRes.ProjectID = dataObject.value("ProjectID").toString();
	queryRes.ConnectService = dataObject.value("ConnectService").toString();
	queryRes.ProjectStatus = dataObject.value("ProjectStatus").toString();

	return resCode;
}

int HttpUtils::deployAiInstance(QueryAiUrlReq& queryAiUrlReq, QueryAiUrlRes& queryRes)
{
	int resCode;
	QString resbody;
	QString uri = QStringLiteral("/iqi/ProjectService/V2/project");
	// build JSON Req
	QJsonObject reqJson;
	reqJson.insert("SerialNumber", getSerialNumber());
	reqJson.insert("ProductName", queryAiUrlReq.ProductName);
	reqJson.insert("SceneName", queryAiUrlReq.SceneName);
	QJsonDocument document;
	document.setObject(reqJson);
	QByteArray byteArray = document.toJson(QJsonDocument::Compact);
	QString strJson(byteArray);
	qInfo() << "[POST]Req:" << strJson;

	httpUtils.setScene(queryAiUrlReq.SceneName);
	sendRequest(uri, HttpRequestType::POST, resbody, resCode, byteArray);

	QJsonDocument responseJson = QJsonDocument::fromJson(resbody.toLocal8Bit().constData());
	QJsonObject bodyObj = responseJson.object();
	queryRes.errorCode = bodyObj.value("code").toInt();
	queryRes.descMsg = bodyObj.value("msg").toString();
	QJsonObject dataObject = bodyObj.value("data").toObject();
	queryRes.ProjectName = dataObject.value("ProjectName").toString();
	queryRes.TaskName = dataObject.value("TaskName").toString();
	queryRes.ProjectID = dataObject.value("ProjectID").toString();
	queryRes.ConnectService = dataObject.value("ConnectService").toString();
	queryRes.ProjectStatus = dataObject.value("ProjectStatus").toString();

	return resCode;
}

// get Max_Time_Limit of connection
int HttpUtils::getConnectionTimeOut()
{
	int timeOutInSecond = 0;

	QString timeOutInSecondStr = readGlobalIniConfig("Global/ConnectionTimeOut");
	if (timeOutInSecondStr.isEmpty()) {
		qInfo() << "ConnectionTimeOut loaded from ini_config is Empty,set ConnectionTimeOut to 6";
		writeGlobalIniConfig("Global/ConnectionTimeOut", "6");
		return 6;
	}
	try
	{
		timeOutInSecond = timeOutInSecondStr.toInt();
	}
	catch (const std::exception&)
	{
		qCritical() << "ConnectionTimeOut got from ini_config is not Str";
		timeOutInSecond = 6;
	}
	return timeOutInSecond;
}

/// <summary>
/// send Infer Request to Infer_Service
/// 
/// note: need to set scene name
/// </summary>
/// <param name="url"> address of Infer</param>
/// <param name="scene">scene name</param>
/// <param name="reqJson">json string of request</param>
/// <param name="rspJson">json string of response，reference type</param>
/// <returns></returns>
int HttpUtils::sendInferRequestCommon(QString url, QString scene, QString reqJson, QString& rspJson) 
{
	this->setScene(scene);
	return this->sendInferRequestCommon(url, reqJson, rspJson);
}


/// <summary>
/// send Infer Request to Infer_Service
/// 
/// note: need to set scene name
/// </summary>
/// <param name="url"> address of Infer</param>
/// <param name="reqJson">json string of request</param>
/// <param name="rspJson">json string of response，reference type</param>
/// <returns>curl response code</returns>
int HttpUtils::sendInferRequestCommon(QString url, QString reqJson, QString& rspJson)
{
	int rspCode;
	bool debugLogRequired = util::HwUtils::logDebugRequired();
	std::unique_ptr<RestClient::Connection> conn;
	conn = std::make_unique<RestClient::Connection>(url.toLocal8Bit().constData());
	std::string contentType = "application/json";
	conn->AppendHeader("Content-Type", contentType);

	std::string uuid = util::HwUtils::getBiosId().toLocal8Bit().constData();
	conn->AppendHeader(REQ_HEADER_KEY_UUID, uuid);
	qInfo() << "header uuid:" << uuid.c_str();

	std::string reqIp = util::HwUtils::read_ip_address().toLocal8Bit().constData();
	conn->AppendHeader(REQ_HEADER_KEY_IP, reqIp);
	qInfo() << "header ip:" << reqIp.c_str();

	conn->AppendHeader(REQ_HEADER_KEY_VERSION_SOFTWARE, REQ_HEADER_VALUE_VERSION_SOFTWARE);
	qInfo() << "header vision-software:" << REQ_HEADER_VALUE_VERSION_SOFTWARE;

	QString scene = getScene();
	conn->AppendHeader(REQ_HEADER_KEY_SCENE_NAME, scene.toLocal8Bit().constData());
	qInfo() << "header_scene-name:" << scene;

	std::string authValue = "Bearer ";
	authValue.append(getToken().toLocal8Bit().constData());
	conn->AppendHeader("Authorization", authValue);
	if (debugLogRequired)
	{
		qDebug() << "Authorization:" << authValue.c_str();
	}
	conn->SetTimeout(getConnectionTimeOut());
	conn->SetCAInfoFilePath(m_SslParam.CAFilePath.toLocal8Bit().constData());

	if (debugLogRequired)
	{
		util::HwUtils::simpleLog2File(QString("%1_Req.txt").arg(scene), reqJson.toLocal8Bit().constData());
	}
	//由于postURL已包含全部，所以""为空。
	RestClient::Response  response = conn->post("", reqJson.toLocal8Bit().constData());
	rspJson = QString::fromLocal8Bit(response.body.c_str(), response.body.size());
	rspCode = response.code;
	if (debugLogRequired)
	{
		qDebug() << "Rsp:" << QString(" (%1) ").arg(response.code) << response.body.c_str();
		util::HwUtils::simpleLog2File(QString("%1_Rsp.txt").arg(scene), QString("(%1):%2").arg(response.code).arg(rspJson.toLocal8Bit().constData()));
	}
	if (!util::HwUtils::checkHttpCode(rspCode,response.body.c_str()))
	{
		return rspCode;
	}
	if (rspJson.toLower().contains("error") || rspJson.toLower().contains("timeout"))
	{
		qCritical() << "Rsp Error:" << rspJson.toLocal8Bit().constData();
	}
	return rspCode;
}

/// <summary>
/// 
/// </summary>
/// <param name="url"> address of Infer</param>
/// <param name="sceneTag">scene tag,Enum</param>
/// <param name="req">AoiReq Object,reference type</param>
/// <param name="detPlusRsp">DetPlusRsp Object,reference type</param>
/// <returns></returns>
int HttpUtils::sendInferReq(QString url,SceneTag sceneTag, AoiReq& req, DetPlusRsp& detPlusRsp, QString &rspJson)
{
	int rspCode;
	rspCode = sendInferRequestCommon(url, req.toJsonQString(), rspJson);
	QStringList fileNameList;
	QListIterator<ReqFile> iter(req.reqFiles);
	while (iter.hasNext())
	{
		fileNameList.append(iter.next().fileName);
	}
	DetPlusRspParser parser(sceneTag);
	parser.parseDetPlusOtherRsp(fileNameList, rspJson, detPlusRsp);
	return rspCode;

}

/// <summary>
/// Det+Det Infer
/// 
/// post  DetPlusDet Request
/// </summary>
/// <param name="url"> address of Infer</param>
/// <param name="req">AoiReq Object,reference type</param>
/// <param name="detPlusRsp">DetPlusRsp Object,reference type</param>
/// <returns></returns>
int HttpUtils::sendInferReq_DetPlusDet(QString url, AoiReq& req, DetPlusRsp& detPlusRsp, QString &rspJson)
{
	//this->setScene(STR_DET_DET_CLS_SCENE_NAME);
	this->setScene(STR_DET_CLS_SCENE_NAME);
	return this->sendInferReq(url, SceneTag::DET_PLUS_DET_CLS, req, detPlusRsp, rspJson);
}

int HttpUtils::sendInferReq_DetPlusSeg(QString url, aoi::AoiReq& req, aoi::DetPlusRsp& detPlusRsp, QString &rspJson)
{
	//this->setScene(STR_DET_SEG_SCENE_NAME);
	this->setScene(STR_SEG_SCENE_NAME);
	return this->sendInferReq(url, SceneTag::DET_PLUS_SEG, req, detPlusRsp, rspJson);
}

/// <summary>
/// Det+OCR Infer
/// 
/// post  DetPlusOcr Request
/// </summary>
/// <param name="url"> address of Infer</param>
/// <param name="req">AoiReq Object,reference type</param>
/// <param name="detPlusRsp">DetPlusRsp Object,reference type</param>
/// <returns></returns>
int HttpUtils::sendInferReq_DetPlusOcr(QString url, AoiReq& req, DetPlusRsp& detPlusRsp, QString &rspJson)
{
	//this->setScene(STR_DET_OCR_SCENE_NAME);
	this->setScene(STR_OCR_SCENE_NAME);
	return this->sendInferReq(url, SceneTag::DET_PLUS_OCR, req, detPlusRsp, rspJson);
}
